#include <stdlib.h>
#include "spi_flash_en25f80.h"

//*******************************************************************/
//Function: Read FLASH Status Reg
//*******************************************************************/
int spi_flash_read_sr(void)
{
    unsigned char val;
    CS_L;
    SEND_CMD(0x05);
    GET_DATA(val);
    CS_H;
    return val;
}

//*******************************************************************/
//Function: Write FLASH Status Reg
//*******************************************************************/
int spi_flash_write_sr(char sr)
{
    unsigned char res,val;
    spi_flash_set_wren();
    spi_flash_wait_sr();
    CS_L;
    SEND_CMD(0x1);
    SEND_DATA(sr);
    CS_H;
    return 1;
}

//*******************************************************************/
//Function: Wait FLASH Status Reg WIP (Device Busy)
//*******************************************************************/
int spi_flash_wait_sr(void)
{
    unsigned char val;

    CS_L;
    SEND_CMD(0x05);
    do{
        GET_DATA(val);
    }while(val&0x1);
    CS_H;
    return val;
}

//*******************************************************************/
//Function: Set FLASH Write Enable
//*******************************************************************/
int spi_flash_set_wren(void)
{
    unsigned char val;
    spi_flash_wait_sr();
    CS_L;
    SEND_CMD(0x6);
    CS_H;

    CS_L;
    SEND_CMD(0x05);
    do{
        GET_DATA(val);
    }while(!(val&0x2));
    CS_H;
    return 1;
}

//*******************************************************************/
//Function: Read FLASH ID
//*******************************************************************/
void spi_flash_read_id(void)
{
    unsigned char val;
    spi_flash_wait_sr();
    CS_L;
    SEND_CMD(0x9F);
    GET_DATA(val); 
    printf("Manufacturer's ID:         %x\n",val);
    GET_DATA(val); 
    printf("Device ID-memory_type:     %x\n",val);
    GET_DATA(val); 
    printf("Device ID-memory_capacity: %x\n",val);
    CS_H;
}

//*******************************************************************/
//Function: Erase FLASH All Memory
//*******************************************************************/
int spi_flash_erase_all(void)
{
    int i=1,val;

    spi_flash_write_sr(0);
    spi_flash_set_wren();
    spi_flash_wait_sr();

    CS_L;
    SEND_CMD(0xc7);
    CS_H;

    while(i++){
        if(spi_flash_read_sr() & 0x3){
            //if(i % 10000 == 0) printf(".");
        }else{
            //printf("done...\n");
            break;
        }   
    }
    return 1;
}

//*******************************************************************/
//Function: Erase FLASH Memory
//*******************************************************************/
int spi_flash_erase_area(unsigned int saddr,unsigned int eaddr,unsigned sectorsize)
{
    unsigned int addr;

    for(addr=saddr;addr<eaddr;addr+=sectorsize)
    {
        spi_flash_set_wren();
        spi_flash_wait_sr();

        CS_L;
        SEND_CMD(0xd8);
        SEND_DATA(addr>>16);
        SEND_DATA(addr>>8);
        SEND_DATA(addr);
        CS_H;
    }
    spi_flash_wait_sr();
    return 0;
}

//*******************************************************************/
//Function: Write a Byte to the FLASH Address
//*******************************************************************/
void spi_flash_write_byte(unsigned int addr,unsigned char data)
{
    /*byte_prog$31m,CE 0, cmd 0x2,addr2,addr1,addr0,data in,CE 1*/
    unsigned char addr2,addr1,addr0;
    unsigned char val;
    addr2 = (addr & 0xff0000)>>16;
    addr1 = (addr & 0x00ff00)>>8;
    addr0 = (addr & 0x0000ff);
    spi_flash_set_wren();
    spi_flash_wait_sr();
    CS_L;
    SEND_CMD(0x2);
    SEND_DATA(addr2);
    SEND_DATA(addr1);
    SEND_DATA(addr0);
    SEND_DATA(data);
    CS_H;
}

//*******************************************************************/
//Function: Write FLASH One Page Memory
//*******************************************************************/
void spi_flash_write_page(unsigned int addr,unsigned char *buf,unsigned int num)
{
    /*byte_prog$31m,CE 0, cmd 0x2,addr2,addr1,addr0,data in,CE 1*/
    unsigned char addr2,addr1,addr0;
    unsigned int i,real_num;
    addr2 = (addr & 0xff0000)>>16;
    addr1 = (addr & 0x00ff00)>>8;
    addr0 = (addr & 0x0000ff);
    spi_flash_set_wren();
    //spi_flash_wait_sr();
    real_num = min(num,SPIF_PAGE_SIZE);

    CS_L;
    SEND_CMD(0x2);
    SEND_DATA(addr2);
    SEND_DATA(addr1);
    SEND_DATA(addr0);
    for(i=0;i<real_num;i++){
        SEND_DATA(buf[i]);
    }
    CS_H;

    spi_flash_wait_sr();
	printf(".");
}

//*******************************************************************/
//Function: Write buffer Data to FLASH Memory
//*******************************************************************/
int spi_flash_write_area(int flashaddr,char *buffer,int size)
{
    int j,i;

    spi_flash_write_sr(0x00);
    CS_L;
    if(1){
        for(j=0;(size > 0 &&(flashaddr & (SPIF_PAGE_SIZE -1 )));flashaddr++,size--,j++){
            spi_flash_write_byte(flashaddr,*buffer++);
        }
        for(;size >= SPIF_PAGE_SIZE;){
            spi_flash_write_page(flashaddr,buffer,SPIF_PAGE_SIZE);
            buffer += SPIF_PAGE_SIZE;
            flashaddr += SPIF_PAGE_SIZE;
            size -= SPIF_PAGE_SIZE;
            j += SPIF_PAGE_SIZE;
        } 
        spi_flash_write_page(flashaddr,buffer,size);
    }else{
        for(j=0;size > 0;flashaddr++,size--,j++)
        {
            //        if((j&0xffff)==0)printf("%x\n",j);
            spi_flash_write_byte(flashaddr,buffer[j]);
        }
    }
    spi_flash_wait_sr();
    CS_H;
    return 0;
}

//*******************************************************************/
//Function: Read a Byte from the FLASH Address
//*******************************************************************/
uint8_t spi_flash_read_byte(int flashaddr)
{
    uint8_t buffer;
    CS_L;
    SEND_CMD(0x03);
    SEND_DATA(flashaddr>>16);
    SEND_DATA(flashaddr>>8);
    SEND_DATA(flashaddr);
    GET_DATA(buffer);
    CS_H;

    return buffer;
}

//*******************************************************************/
//Function: Read FLASH Memory to buffer
//size: number of bytes
//*******************************************************************/
int spi_flash_read_area(int flashaddr,char *buffer,int size)
{
    int i;

    CS_L;
    SEND_CMD(0x03);
    SEND_DATA(flashaddr>>16);
    SEND_DATA(flashaddr>>8);
    SEND_DATA(flashaddr);
    for(i=0;i<size;i++)
    {
        GET_DATA(buffer[i]);
    }
    CS_H;

    return 0;
}

//*******************************************************************/
//Function: Erase And Write FLASH Memory
//size: number of bytes
//*******************************************************************/
int spi_flash_erase_write(unsigned int ramaddr, unsigned int flashaddr, unsigned int size)
{
    spi_flash_erase_area(flashaddr,flashaddr+size,0x10000);
    printf("\nfrom ram 0x%08x to flash 0x%08x size 0x%08x \n",ramaddr,flashaddr,size);
    printf("programming...");
    spi_flash_write_area(flashaddr, (char *)ramaddr, size);
    printf("\nend\n");
    return 1;
}
